﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using nanoCMS.Core.Concrete;
using nanoCMS.Core.Helpers;
using nanoCMS.Core.Interfaces;
using nanoCMS.Core.Providers;

using System.Net;
using System.Net.Mail;

namespace nanoCMS.Core.Membership
{
    public enum MembershipReturn
    {
        EntitieNotFoundInDb,
        Success,
        DbError,
        UserAlreadyLoggedIn,
        UsernameAndPasswordMissmatch,
        EmailInUse
    }


    public class UserProvider : IUserProvider
    {
        private const string UserCacheKey = "UserData";

        private CacheProvider cacheProvider = CacheProvider.Default;
        private readonly System.Web.HttpContext httpContext;
        private readonly Entities entities;
        private MessageProvider messageProvider;
        private EmailHelper emailHelper;

        public UserProvider(Entities entities, System.Web.HttpContext httpContext)
        {
            this.entities = entities;
            this.httpContext = httpContext;
        }

        #region security stuff
        public MembershipReturn CheckAndGetUser(string email, string password, out User user)
        {
            password = System.Web.Helpers.Crypto.SHA1(password);
            user = entities.Users.SingleOrDefault(x => x.Email == email && x.Password == password);
            return (user != null) ? MembershipReturn.Success : MembershipReturn.EntitieNotFoundInDb;
        }

        public MembershipReturn CheckUser(string email, string password)
        {
            User user;
            CheckAndGetUser(email, password, out user);
            return (user != null) ? MembershipReturn.Success : MembershipReturn.EntitieNotFoundInDb;
        }

        public MembershipReturn LogOn(string email, string password, out User user)
        {
            MembershipReturn status = CheckAndGetUser(email, password, out user);
            if (status != MembershipReturn.Success) return status;

            LogIn(user.UserID);
            //user.DisplayName = GetUserDisplayName(user);
            SessionManagerBase.CurrentUser = user;
            PutUserRolesInSession(user.UserID);
            return MembershipReturn.Success;
        }


        private MembershipReturn LogIn(int userId)
        {
            if (httpContext != null && !httpContext.User.Identity.IsAuthenticated)
            {
                System.Web.Security.FormsAuthenticationTicket tkt;
                string cookiestr;
                System.Web.HttpCookie ck;

                tkt = new System.Web.Security.FormsAuthenticationTicket(userId.ToString(), true, 60);
                cookiestr = System.Web.Security.FormsAuthentication.Encrypt(tkt);
                ck = new System.Web.HttpCookie(System.Web.Security.FormsAuthentication.FormsCookieName, cookiestr);
                if (true) ck.Expires = tkt.Expiration;
                ck.Path = System.Web.Security.FormsAuthentication.FormsCookiePath;
                httpContext.Response.Cookies.Add(ck);
                return MembershipReturn.Success;
            }
            return MembershipReturn.UserAlreadyLoggedIn;
        }

        public bool LogOut()
        {
            if (httpContext.User.Identity.IsAuthenticated)
            {
                SessionManagerBase.CurrentUser = null;
                SessionManagerBase.CurrentUserRoles = null;
                System.Web.Security.FormsAuthentication.SignOut();
                return true;
            }
            return false;
        }


        private void PutUserRolesInSession(int userId)
        {
            UserRoleProvider userRoleProvider = new UserRoleProvider(entities);
            SessionManagerBase.CurrentUserRoles = userRoleProvider.GetUserRoles(userId);
        }
        #endregion

        #region CRUD
        public MembershipReturn CreateUser(string email, string password, string username, out User user, bool isActivated = false)
        {
            throw new NotImplementedException();
        }

        public MembershipReturn CreateUser(string email, string password, string username, out User user)
        {
            //If user can't be added
            MembershipReturn status = CreateUser(email, password, username, username, out user, false);
            if (status != MembershipReturn.Success) return status;


            if (Settings.RequireMailAuthorisation || Settings.RequireAdminAuthorisation)
            {
                if (Settings.RequireMailAuthorisation)
                {
                    Dictionary<string, string> userInfo = new Dictionary<string, string> { { "userID", user.UserID.ToString() }, { "username", username }, { "password", password }, { "email", email } };
                    String message = messageProvider.GetProcessedMessage("Core", "en", userInfo).Content;

                    emailHelper.SendEmail(email, "nanoCMSactivation", message);
                }

                if (Settings.RequireAdminAuthorisation)
                {
                    //not activated
                }

            }
            else
            {
                ActivateUser(user.UserID);
            }

            return MembershipReturn.Success;
        }

        public MembershipReturn CreateUser(string email, string password, string username, string displayName, out User user, bool isActivated)
        {
            user = new User();
            if (IsEmailInUse(email)) return MembershipReturn.EmailInUse;

            user = new User
            {
                Email = email,
                Password = System.Web.Helpers.Crypto.SHA1(password),
                Username = username,
                DisplayName = displayName,
                IsActivated = isActivated,
                IsBlocked = false,
                CreatedDate = DateTime.Now
            };
            entities.Users.AddObject(user);
            entities.SaveChanges();
            return MembershipReturn.Success;
        }
        #endregion

        #region Helpers
        public string GetUserDisplayName(User user)
        {
            string DisplayName = String.Empty;
            DisplayName = user.Email;
            if (!String.IsNullOrWhiteSpace(user.Username)) DisplayName = user.Username;

            if (!String.IsNullOrWhiteSpace(user.DisplayName)) DisplayName = user.DisplayName;

            return DisplayName;
        }
        #endregion

        public MembershipReturn ChangeUserPassword(int userId, string newPassword)
        {
            User user = entities.Users.SingleOrDefault(x => x.UserID == userId);
            if (user != null)
            {
                user.Password = System.Web.Helpers.Crypto.SHA1(newPassword);
                entities.SaveChanges();
                cacheProvider.Remove(UserCacheKey + user.UserID);
                return MembershipReturn.Success;
            }
            else
            {
                return MembershipReturn.DbError;
            }
        }

        public MembershipReturn EditUserDisplayName(int userId, string displayName)
        {
            User user = entities.Users.SingleOrDefault(x => x.UserID == userId);
            if (user != null)
            {
                user.DisplayName = displayName;
                entities.SaveChanges();
                cacheProvider.Remove(UserCacheKey + user.UserID);
                SessionManagerBase.CurrentUser.DisplayName = displayName; 
                return MembershipReturn.Success;
            }
            else
            {
                return MembershipReturn.DbError;
            }
        }


		 public bool IsUserNameInUse(string username, int excludeUserId = 0)
		 {
			 if (excludeUserId == 0)
			 	return entities.Users.Any(x => x.Username == username);
		 	return entities.Users.Any(x => x.Username == username && x.UserID != excludeUserId);
		 }


        public bool IsEmailInUse(string email, int excludeUserID = 0)
        {
            if (excludeUserID == 0)
                return entities.Users.Any(x => x.Email == email);
            return entities.Users.Any(x => x.Email == email && x.UserID != excludeUserID);
        }

        public MembershipReturn GetUser(int userId, out User user)
        {
            if (!cacheProvider.Contains(UserCacheKey + userId))
            {
                user = entities.Users.SingleOrDefault(x => x.UserID == userId);
                if (user != null)
                {
                    cacheProvider.Add(UserCacheKey + userId, user);
                }
            }
            else
                user = cacheProvider.Get(UserCacheKey + userId) as User;

            return (user != null) ? MembershipReturn.Success : MembershipReturn.EntitieNotFoundInDb;
        }

        public MembershipReturn GetUser(string email, out User user)
        {
            user = entities.Users.SingleOrDefault(x => x.Email == email);
            return (user != null) ? MembershipReturn.Success : MembershipReturn.EntitieNotFoundInDb;
        }

        public MembershipReturn ActivateUser(int userId)
        {
            User user;
            if (GetUser(userId, out user) != MembershipReturn.Success) throw new ArgumentOutOfRangeException();
            user.IsActivated = true;
            entities.SaveChanges();
            return MembershipReturn.Success;
        }

        public MembershipReturn BlockUser(int userId, string reason)
        {
            throw new NotImplementedException();
        }

        public MembershipReturn UnblockUser(int userId)
        {
            throw new NotImplementedException();
        }

        public MembershipReturn LogIn(string email)
        {
            throw new NotImplementedException();
        }

        public List<User> GetUsers(bool? isActivated = new bool?(), bool? isBlocked = new bool?())
        {
            return entities.Users.ToList();
        }

    }
}
